package net.crowtheries.wirelessrouting;

import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import java.util.HashMap;
import java.util.HashSet;
import net.crowtheries.wirelessrouting.messages.*;

import ptolemy.actor.TypedAtomicActor;
import ptolemy.data.BooleanToken;
import ptolemy.data.IntToken;
import ptolemy.data.RecordToken;
import ptolemy.data.StringToken;
import ptolemy.data.Token;
import ptolemy.data.expr.StringParameter;
import ptolemy.domains.wireless.kernel.WirelessIOPort;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;

public class ProtocolManager extends TypedAtomicActor{
	public ProtocolManager(CompositeEntity container,String name) throws IllegalActionException, NameDuplicationException{
		super(container,name);
		_dstSeq=new HashMap<String, Integer>();
        _RREQCache=new HashSet<RREQ>();
        _rreqBuffer=new HashMap<String,Object[]>();

		reqRoute=new WirelessIOPort(this,"reqRoute",true,false);
        toFlowHandler=new WirelessIOPort(this,"toFlowHandler",false,true);
        toRoutesManager=new WirelessIOPort(this,"toRoutesManager",false,true);
        fromFlowHandler=new WirelessIOPort(this,"fromFlowHandler",true,false);
        entryStatus=new WirelessIOPort(this,"entryStatus",true,false);
	}
	
	//////Private vars////
	private int _localSeq;
	private HashMap<String, Integer> _dstSeq;
    /**
     * Key: IP
     * Value: [ PREVHOP , RREQ ]
     */
    private HashMap<String,Object[]> _rreqBuffer;
    private HashSet<RREQ> _RREQCache;
	private String _localIP;
	private int _RREQID=0;
	
	///////PORTS//////
	public WirelessIOPort reqRoute;
    public WirelessIOPort toRoutesManager;
    public WirelessIOPort entryStatus;
    public WirelessIOPort toFlowHandler;
    public WirelessIOPort fromFlowHandler;
	
	public void initialize(){
		StringParameter localip=(StringParameter)this.getContainer().getAttribute("localIP");
		_localIP=localip.getValueAsString();
	}
	public void fire() throws IllegalActionException{
		/**
         * B.I
		 * If we receive a DSTIP from the RoutesManager, we make a RREQ 
		 */
		if(reqRoute.hasToken(0)){
			//Destination IP
			String dstIP=((StringToken)reqRoute.get(0)).stringValue();			
			//Destination SEQ
			//TODO:add the possibility to get the DSTSEQ from the cache _dstSeq
            //NOTE:dstSEQ=-1 indique qu'on n'a aucune info sur le seq number
			int dstSEQ=-1;

            RREQ request=new RREQ(_localIP,_localSeq,dstIP,dstSEQ,newRREQID());
            _RREQCache.add(request);
            toFlowHandler.send(0, request.getRequestToken());
		}

        /**
         * B.II
         * If we receive a message from the FlowHandler
         */
        if(fromFlowHandler.hasToken(0)){
            RecordToken message=(RecordToken)fromFlowHandler.get(0);
            String PREVHOP=((StringToken)message.get("DSTIP")).stringValue();
            message=(RecordToken)message.get("SEG");
            message=(RecordToken)message.get("DATA");
            int messageType=((IntToken)message.get("MSGTYPE")).intValue();
            message=(RecordToken)message.get("MSG");
            switch(messageType){
                //RREQ message
                case 1:
                    RREQ req=new RREQ(message);

                    //Si le message n'est pas nouveau !!!
                    if(_RREQCache.contains(req))
                        break;

                    //On incrémente le HOPCOUNT car le message a fait un saut:
                    req.incHop();

                    //TODO:Ajouter vérification que le message est nouveau
                    _RREQCache.add(req);
                    toRoutesManager.send(0, this.newRouteManagementUpdateToken(
                            3,
                            req.getSRCIP(),
                            PREVHOP,
                            req.getSRCSEQ(),
                            req.getHOPCOUNT()
                            ));
                    if(req.getDSTIP().equals(_localIP)){
                        //TODO: we'll have to send a RREP to the PREVHOP

                    } else {
                        //we check if we have a route to the destination
                        toRoutesManager.send(0, this.newRouteManagementToken(4,
                                new StringToken(req.getDSTIP())));

                        //we cache the IP so we use it when we get the answer
                        _rreqBuffer.put(req.getDSTIP(),new Object[]{
                            PREVHOP,
                            req
                        });

                        //The result will come in B.III
                    }
                    break;
                case 2:
                    break;
                case 3:
                    break;
            }
        }

        /**
         * B.III
         * We get the results from RoutesManager about entries status here
         */
        if(entryStatus.hasToken(0)){
            RecordToken message=(RecordToken)entryStatus.get(0);
            String ip=((StringToken)message.get("IP")).stringValue();
            boolean result=((BooleanToken)message.get("STATUS")).booleanValue();
            //if the result was requested from the RREQ process:
            if(_rreqBuffer.containsKey(ip)){
                Object[] data=_rreqBuffer.get(ip);
                String PREVHOP=(String)data[0];
                RREQ req=(RREQ)data[1];
                if(result){
                    //we have a route to DST
                    //TODO:Send RREP to PREVHOP

                }else{
                    //we dont have a route to DST
                    toFlowHandler.send(0, req.getRequestToken());

                }
                _rreqBuffer.remove(ip);
            }
        }


	}

    private RecordToken newRouteManagementToken(int OPC, Token message) throws IllegalActionException{
        RecordToken token=new RecordToken(new String[]{
            "OPC",
            "MSG"
        },new Token[]{
            new IntToken(OPC),
            message
        });
        return token;
    }

    private RecordToken newRouteManagementUpdateToken(int OPC,String IP, String NEXTHOP, int DSTSEQ, int HOPCOUNT)
            throws IllegalActionException{
        RecordToken message=new RecordToken(new String[]{
            "IP",
            "NEXTHOP",
            "DSTSEQ",
            "HOPCOUNT"
        },new Token[]{
            new StringToken(IP),
            new StringToken(NEXTHOP),
            new IntToken(DSTSEQ),
            new IntToken(HOPCOUNT)
        });

        return newRouteManagementToken(OPC, message);
    }

    private int newRREQID(){
        _RREQID++;
        if(_RREQID==65536)
            _RREQID=1;
        return _RREQID;
    }

}
